package My

import (
	"database/sql"
	"errors"
	"fmt"
	"db2s/ETL"
	"db2s/global"
	"reflect"
	"strconv"
	"strings"
	"time"
)

type ConnActive struct {
}

func singleDbSyncParameter() map[string] /*parameter type*/ any /*parameter info*/ {
	return map[string]any{"static": rowsStaticParameter, "dynamic": rowsDynamicParameter}
}
func singleDbStructParameter() map[string] /*parameter type*/ any /*parameter info*/ {
	return map[string]any{"static": metaStaticParameter, "dynamic": metaDynamicParameter}
}
func modifyParameter(mode string) (returnValue map[string]any) {
	Func, ok := classModeParameter[mode]
	if !ok {
		return
	}
	if Func.Kind() != reflect.Func { // 确保 Func 是一个函数
		return
	}
	returnValue = Func.Call(nil)[0].Interface().(map[string]any)
	return
}

// parameterQuery 参数查询
func parameterQuery(s global.DbParameterOptimizerS, pName string) (result string, err error) {
	var event = "[MySQLParameterQuery]"
	var f1 any
	querySql := fmt.Sprintf("select VARIABLE_VALUE from performance_schema.global_variables where VARIABLE_NAME='%v'", pName)
	if _, f1, err = ExecSelectSql(ExecSqlInputS{
		Ss:           s,
		Event:        event,
		SelectSqlStr: querySql,
		Expect:       ETL.String,
	}); err != nil || f1 == nil {
		return
	}
	result = f1.(string)
	return
}

// parameterSet 参数设置
func parameterSet(s global.DbParameterOptimizerS, pName string, pValue any) (err error) {
	setSql := fmt.Sprintf("set global %v = %v", pName, pValue)
	if _, err = ExecUpdateSql(ExecSqlInputS{
		Ss:           s,
		UpdateSqlStr: []string{setSql},
	}); err != nil {
		return
	}
	return
}

func modifyDbParameter(s global.DbParameterOptimizerS, timeStamp string, pInfo map[string]any) (result []global.ModifyParameterResultS, err error) {
	var b int
	for variablesName, expect := range pInfo {
		b++
		expectValue := fmt.Sprintf("%v", expect)
		res := global.ModifyParameterResultS{
			Object:        "variables",
			Time:          timeStamp,
			Mode:          s.Con.Mode,
			Seq:           strconv.Itoa(b),
			ShardName:     s.ShardName,
			ParameterName: variablesName,
			NewValue:      expectValue,
		}
		if res.OldValue, err = parameterQuery(s, variablesName); err != nil {
			continue
		}
		if strings.EqualFold(expectValue, res.OldValue) {
			continue
		}
		if parameterSet(s, variablesName, res.NewValue) != nil {
			continue
		}
		res.Status = true
		res.Con = s.Con
		result = append(result, res)
	}
	return
}
func parameterOptimization(c global.Conn, db map[string]*sql.DB) (result []global.ModifyParameterResultS) {
	timeStamp := time.Now().Format("2006-01-02T15:04:05")
	pInfo, ok := modifyParameter(c.Mode)["dynamic"]
	if !ok {
		return
	}
	for shard := range db {
		s := global.DbParameterOptimizerS{
			DB:        db,
			ShardName: shard,
			Con:       c,
		}
		res, _ := modifyDbParameter(s, timeStamp, pInfo.(map[string]any))
		result = append(result, res...)
	}
	return
}
func getNlsCharacterSet(s global.Conn) (res string, err error) {
	var (
		db    *sql.DB
		f1    any
		event = "[MySQLGetNlsCharacterSet]"
	)
	if db, err = sql.Open(s.Device, s.Jdbc); err != nil {
		return
	}
	r := global.DBGetCharacterS{
		DB: map[string]*sql.DB{"single": db},
		Input: global.StartPartConfigInputP{
			SqlMode: true,
		},
	}
	strSql := fmt.Sprintf("select VARIABLE_VALUE as `character` from performance_schema.global_variables where VARIABLE_NAME='character_set_server'")
	if _, f1, err = ExecSelectSql(ExecSqlInputS{
		Ss:           r,
		Event:        event,
		SelectSqlStr: strSql,
		Expect:       ETL.String,
	}); err != nil || f1 == nil {
		return
	}
	res = f1.(string)
	return
}
func jdbcSpl(s global.Conn) (res string) {
	var (
		parameterStr, jdbcNew string
		parameter             []string
	)
	res = s.Jdbc
	return res
	//root:Passw0rd@tcp(172.17.136.79:5506)/information_schema?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai&maxAllowedPacket=0"
	if n := strings.Index(s.Jdbc, "?"); n != -1 {
		jdbcNew = s.Jdbc[:n]
		parameterStr = s.Jdbc[n+1:]
	} else {
		jdbcNew = s.Jdbc
	}
	if strings.Contains(parameterStr, "&") {
		parameter = strings.Split(parameterStr, "&")
	}
	if len(parameter) == 0 {
		parameter = s.JdbcExpandValue
	}
	//连接信息
	if len(parameter) > 0 {
		res = fmt.Sprintf("%v?%v", jdbcNew, strings.Join(parameter, "&"))
	}
	return
}

func jdbcExpandFunc(s global.Conn) (JdbcExpandValue []string) {
	var (
		charset string
		err     error
	)
	if !strings.Contains(s.Jdbc, "charset=") {
		if charset, err = getNlsCharacterSet(s); err == nil {
			JdbcExpandValue = append(JdbcExpandValue, fmt.Sprintf("charset=%v", charset))
		}
	}
	if !strings.Contains(s.Jdbc, "maxAllowedPacket=") {
		JdbcExpandValue = append(JdbcExpandValue, fmt.Sprintf("maxAllowedPacket=0"))
	}
	if !strings.Contains(s.Jdbc, "loc=") {
		JdbcExpandValue = append(JdbcExpandValue, fmt.Sprintf("loc=Asia%%2FShanghai"))
	}
	if !strings.Contains(s.Jdbc, "parseTime=") {
		JdbcExpandValue = append(JdbcExpandValue, fmt.Sprintf("parseTime=True"))
	}
	return
}
func maxConnectionSum(s global.DbParameterOptimizerS) (maxAvailableConnection int64, err error) {
	var (
		f1    any
		event = "[MySQLMaxConnectionSum]"
	)
	strSql := fmt.Sprintf("select VARIABLE_VALUE-(select count(1) from information_schema.processlist) as `maxAvailableConnection` from performance_schema.global_variables where variable_name = 'max_connections'")
	if _, f1, err = ExecSelectSql(ExecSqlInputS{
		Ss:           s,
		Event:        event,
		SelectSqlStr: strSql,
		Expect:       ETL.String,
	}); err != nil || f1 == nil {
		return
	}
	if maxAvailableConnection, err = strconv.ParseInt(fmt.Sprintf("%v", f1), 10, 64); err != nil {
		return
	}
	return
}

/*
连接数据库，返回连接内存地址
*/
func openDb(s global.Conn) (result global.ConnPool, err error) {
	var maxSum int64
	s.JdbcExpandValue = jdbcExpandFunc(s)
	if result.Single, err = sql.Open(s.Device, jdbcSpl(s)); err != nil {
		err = errors.New(fmt.Sprintf("database open fail. case: %v", err))
		return
	}
	if err = result.Single.Ping(); err != nil {
		err = errors.New(fmt.Sprintf("database connection fail. case: %v", err))
		return
	}
	result.Single.SetMaxIdleConns(s.MaxIdle)
	result.Single.SetMaxOpenConns(s.MaxOpen)
	if maxSum, err = maxConnectionSum(global.DbParameterOptimizerS{
		DB:        map[string]*sql.DB{"single": result.Single},
		ShardName: "single",
		Input: global.StartPartConfigInputP{
			SqlMode: true,
		},
	}); err != nil {
		return
	}
	result.Single.SetMaxOpenConns(int(maxSum / 2))
	result.Max = map[string]int64{"single": maxSum}
	result.Conn = map[string]*sql.DB{"single": result.Single}
	return
}

func (c ConnActive) Open(s global.Conn) (result global.ConnPool, err error) {
	var parameterModify []global.ModifyParameterResultS
	//defer func() {
	//	if err1 := recover(); err1 != nil {
	//		fmt.Println("mysql database create session connection fail, Check the database connection information.err info is ", err1)
	//	}
	//}()
	if s.Optimizer {
		if result, err = openDb(s); err != nil {
			return
		} else {
			parameterModify = parameterOptimization(s, result.Conn)
		}
	}
	result, err = openDb(s)
	result.ParameterModify = parameterModify
	return
}

// Close 参数复原
func (c ConnActive) Close(db map[string]*sql.DB) {
	//for k, v := range db {
	//	active := dataDispos.InstanceActiveS{DBType: "mysql", Schema: "information_schema", Table: "", DB: v}
	//	for k1, v1 := range global.ParameterOldValue {
	//		if strings.EqualFold(k, k1) {
	//			for k2, v2 := range v1 {
	//				_, err2 := c.parameterSet(active, k2, v2, k)
	//				if err2 != nil {
	//					//global.Wlog.Error(fmt.Sprintf("(%d) %v %v parameter recovery fail. data is {%v} error info is {%v}", logSeq, Event, k2, v2, err2))
	//				}
	//			}
	//		}
	//	}
	//}
}
